#include "myinet.h"
#include "tools.h"
#include <stdio.h>
#include <stdlib.h>

/**
 * 使用单线程实现echo服务器
 * 基本算法如下
 * 每次一个连接到来，把对应的fd放入到一个set中
 * 然后循环等待这个set中的fd就绪
 * 处理就绪的fd
 * 继续等待
 *
 * 这样的方法适用于大部分时间都被阻塞的传输
 */

#define QLEN 128
#define BUFSIZE 4096

int
echo(int fd) {
    char buf[BUFSIZE];
    int size = read(fd, buf, sizeof(buf));

    if (size < 0)
        error_exit("read", "failed to read from client");

    if (size > 0 && write(fd, buf, size) < 0)
        error_exit("write", "failed to write to client");

    return size;
}

int
main(int argc, char** argv) {

    if (argc < 2) {
        fprintf(stderr, "usage: %s <port>\n", argv[0]);
        exit(-1);
    }

    struct sockaddr_in addr;
    socklen_t addrlen = sizeof(addr);

    int master_sock;
    int slave_sock;
    int portNumber;
    char buf[BUFSIZE] = {0};

    fd_set rfds;
    // active file descriptor set
    fd_set afds;

    portNumber = atoi(argv[1]);

    master_sock = get_server_socket(portNumber, SOCK_STREAM, QLEN);

    if (master_sock < 0) {
        error_exit("get_server_socket", "failed to get socket");
    }

    int fd, nfds;

    // 获取最多fd数量
    nfds = getdtablesize();
    // 清空一个set
    FD_ZERO(&afds);
    // 将套接字加入到set中
    FD_SET(master_sock, &afds);

    info("listening to port: %d",portNumber);
    for (;;){
        // 将active 的fd set复制到可读的fd set
        memcpy(&rfds, &afds, sizeof(rfds));

        // 调用select，阻塞，等待fd可用
        // 只需要等待可读的fd即可
        if (select(nfds, &rfds, NULL, NULL, NULL) < 0)
            error_exit("select", 0);


        // 如果接收到了新的连接
        if (FD_ISSET(master_sock, &rfds)) {
            slave_sock = accept(master_sock, (struct sockaddr*)&addr, &addrlen);
            if (slave_sock < 0)
                error_exit("accept", "error accepting connection");

            // 将新来的连接加入集合中
            FD_SET(slave_sock, &afds);
        }

            // 遍历集合，处理就绪的fd
        for (fd = 0 ; fd < nfds ; fd++) {
            if (fd != master_sock && FD_ISSET(fd, &rfds)) {
                if (echo(fd) == 0) {
                    close(fd);
                    FD_CLR(fd, &afds);
                }
            }
        }

    }

}
